﻿using HJMinimally.AutoMapper;
using HJMinimally.Minio.IServices;
using HJMinimally.Minio.Model;
using HJMinimally.Minio.ViewModel.Req;
using HJMinimally.Minio.ViewModel.Resp;
using HJMinimally.Repository.SqlSugar;
using HJMinimally.Utility;
using HJMinimally.Utility.Common;
using HJMinimally.Utility.Extensions;
using HJMinimally.Utility.Http;
using HJMinimally.Utility.Images;
using Microsoft.AspNetCore.StaticFiles;
using Minio;
using System;
using System.Collections;
using System.IO;
using System.Threading.Tasks;

namespace HJMinimally.Minio.Services
{
    /// <summary>
    /// 文件上传记录服务
    /// </summary>
    public class FileUploadService : IFileUploadService
    {
        private readonly MinioConfigEntity _minioConfigEntity;
        private readonly MinioClient _minioClient;
        private readonly ISqlSugarDbRepository _app;
        private readonly IFileUploadConfigService _fileUploadConfigService;
        public FileUploadService(ISqlSugarDbRepository app,
            IFileUploadConfigService fileUploadConfigService,
            IMinioConfigService minioConfigService
            )
        {
            _app = app;
            _minioConfigEntity = minioConfigService.GetMinioConfig().Result;
            _fileUploadConfigService = fileUploadConfigService;
            _minioClient = new MinioClient(_minioConfigEntity.endpoint, _minioConfigEntity.accesskey, _minioConfigEntity.secretkey);
        }
        /// <summary>
        /// 上传文件
        /// </summary>
        /// <param name="req"></param>
        /// <returns></returns>
        public async Task<Response<UploadFileResp>> UploadFile(UploadFileReq req)
        {
            var result = new Response<UploadFileResp>();
            try
            {
                string bucketName = req.appid;
                byte[] byteData = Utility.Common.File.ToBytes(req.file.OpenReadStream());//上传文件流
                string fileName = Path.GetFileName(req.file.FileName);
                string fileExt = Path.GetExtension(fileName).Trim('.').ToLower();//文件扩展名，不含“.”
                string newFileName = GenerateId.GenerateOrderNumber() + "." + fileExt;//随机生成新的文件名
                string newThumbnailFileName = "thumb_" + newFileName; //随机生成缩略图文件名
                string upLoadPath = GetUpLoadPath(0);//本地上传目录相对路径
                string newFilePath = upLoadPath + newFileName; //本地上传后的路径
                string newThumbnailPath = upLoadPath + newThumbnailFileName; //本地上传后的缩略图路径
                byte[] thumbData = null; //缩略图文件流
                var uploadConfig = await _fileUploadConfigService.GetFileUploadConfigByAppId(req.appid);//获取系统上传配置
                if (uploadConfig == null)
                {
                    result.info = "该系统无上传配置！";
                    return result;
                }
                #region 上传限制校验
                //检查文件字节数组是否为NULL
                if (byteData == null)
                {
                    result.code = ResponseCode.exception;
                    result.info = "请选择要上传的文件！";
                    return result;
                }
                //检查文件扩展名是否合法
                if (!CheckFileExt(fileExt, uploadConfig))
                {
                    result.code = ResponseCode.exception;
                    result.info = $"不允许上传{fileExt}类型的文件！";
                    return result;
                }
                //检查文件大小是否合法
                if (!CheckFileSize(fileExt, byteData.Length, uploadConfig))
                {
                    result.code = ResponseCode.exception;
                    result.info = "文件超过限制的大小！";
                    return result;
                }
                if (IsImage(fileExt, uploadConfig))
                {
                    //检查图片是否超出最大尺寸，是则裁剪
                    if ((uploadConfig.imgmaxheight > 0 || uploadConfig.imgmaxwidth > 0))
                    {
                        byteData = Thumbnail.MakeThumbnailImage(byteData, fileExt, uploadConfig.imgmaxheight.ToInt(), uploadConfig.imgmaxwidth.ToInt());
                    }
                    //检查是否需要生成缩略图，是则生成
                    if (uploadConfig.thumbnailwidth > 0 && uploadConfig.thumbnailheight > 0)
                    {
                        thumbData = Thumbnail.MakeThumbnailImage(byteData, fileExt, uploadConfig.thumbnailwidth.ToInt(), uploadConfig.thumbnailheight.ToInt(), uploadConfig.thumbnailmode);
                    }
                    else
                    {
                        newThumbnailPath = newFilePath; //不生成缩略图则返回原图
                    }
                }
                //检查存储桶是否存在，不存在则创建
                if (!await _minioClient.BucketExistsAsync(bucketName))
                {
                    await _minioClient.MakeBucketAsync(bucketName);
                }
                #endregion
                //上传主文件
                MemoryStream filestream = new MemoryStream(byteData);
                var contentType = GetContentType(fileName);
                await _minioClient.PutObjectAsync(bucketName, newFilePath, filestream, filestream.Length, contentType);
                //上传缩略图文件
                if (thumbData != null)
                {
                    MemoryStream thumbFilestream = new MemoryStream(thumbData);
                    await _minioClient.PutObjectAsync(bucketName, newThumbnailPath, thumbFilestream, thumbFilestream.Length, contentType);
                }
                //文件上传记录入库
                FileUploadEntity entity = new FileUploadEntity()
                {
                    fileid = Guid.NewGuid().ToString(),
                    filename = fileName,
                    filepath = newFilePath,
                    description = "",
                    filesize = byteData.Length,
                    extension = fileExt,
                    contentype = contentType,
                    thumbnail = thumbData != null ? newThumbnailPath : "",
                    appid = req.appid,
                    createtime = DateTime.Now
                };
                await _app.InsertAsync(entity);
                //输出结果调整
                var response = entity.MapTo<UploadFileResp>();
                response.fileurl = _minioConfigEntity.serverurl + "/" + uploadConfig.appid + "/" + response.filepath;
                response.thumbnailurl = response.thumbnail.Equals("") ? "" : _minioConfigEntity.serverurl + "/" + uploadConfig.appid + "/" + response.thumbnail;
                result.data = response;
            }
            catch (Exception ex)
            {
                Log.Logger.Error("[UploadFile:上传文件] Exception：" + ex.ToJson() + "\r\n\r\n");
                Console.WriteLine(ex);
                result.code = ResponseCode.exception;
                result.info = "上传过程中发生意外错误！";
                return result;
            }
            return result;
        }
        /// <summary>
        /// 获取文件信息
        /// </summary>
        /// <param name="id">主键</param>
        /// <returns></returns>
        public async Task<FileUploadEntity> GetFileUploadEntity(string id)
        {
            try
            {
                return await _app.GetModelAsync<FileUploadEntity>(t => t.fileid == id);
            }
            catch (Exception ex)
            {
                if (ex is ExceptionEx)
                {
                    throw;
                }
                else
                {
                    throw ExceptionEx.ThrowBusinessException(ex);
                }
            }
        }
        /// <summary>
        /// 删除服务器文件
        /// </summary>
        /// <param name="req">输入参数</param>
        /// <returns></returns>
        public async Task<Response<bool>> DeleteFileById(DeleteFileByIdReq req)
        {
            var result = new Response<bool>();
            try
            {
                var entity = await _app.GetModelAsync<FileUploadEntity>(t => t.fileid == req.id);
                if (entity == null)
                {
                    result.info = "文件记录不存在";
                    result.data = false;
                    return result;
                }
                await _minioClient.RemoveObjectAsync(entity.appid, entity.filepath);
                result.info = "删除成功！";
                result.data = true;
            }
            catch (Exception ex)
            {
                Log.Logger.Error("[DeleteFileById:删除服务器文件] Exception：" + ex.ToJson() + "\r\n\r\n");
                Console.WriteLine(ex);
                result.code = ResponseCode.exception;
                result.info = "删除失败！";
                result.data = false;
                return result;
            }
            return result;
        }
        /// <summary>
        /// 删除服务器文件
        /// </summary>
        /// <param name="req">输入参数</param>
        /// <returns></returns>
        public async Task<Response<bool>> DeleteFileByPath(DeleteFileByPathReq req)
        {
            var result = new Response<bool>();
            try
            {
                await _minioClient.RemoveObjectAsync(req.appid, req.filepath);
                result.info = "删除成功！";
                result.data = true;
            }
            catch (Exception ex)
            {
                Log.Logger.Error("[DeleteFile:删除服务器文件] Exception：" + ex.ToJson() + "\r\n\r\n");
                Console.WriteLine(ex);
                result.code = ResponseCode.exception;
                result.info = "删除失败！";
                result.data = false;
                return result;
            }
            return result;
        }
        #region 私有方法

        /// <summary>
        /// 返回上传目录相对路径
        /// </summary>
        /// <param name="saveType">0：按年/月/日存入不同目录；1：按年月/日/存入不同目录；2：按年月日每天一个目录</param>
        /// <returns></returns>
        private string GetUpLoadPath(int saveType)
        {
            string path;
            switch (saveType)
            {
                case 0:
                    path = DateTime.Now.Year.ToString() + "/" + DateTime.Now.ToString("MM") + "/" + DateTime.Now.ToString("dd") + "/";//按年月日每天一个目录
                    break;
                case 1:
                    path = DateTime.Now.ToString("yyyyMM") + "/" + DateTime.Now.ToString("dd") + "/";//按年月/日/存入不同目录
                    break;
                case 2:
                    path = DateTime.Now.ToString("yyyyMMdd") + "/";//按年月日每天一个目录
                    break;
                default:
                    path = DateTime.Now.Year.ToString() + "/" + DateTime.Now.Month.ToString() + "/" + DateTime.Now.ToString("dd") + "/";//按年月日每天一个目录
                    break;
            }
            return path;
        }
        /// <summary>
        /// 检查是否为合法的上传文件
        /// </summary>
        /// <param name="_fileExt">文件扩展名，不含“.”</param>
        /// <param name="entity">系统上传配置信息</param>
        /// <returns></returns>
        private bool CheckFileExt(string _fileExt, FileUploadConfigEntity entity)
        {
            //检查危险文件
            string[] excExt = entity.illegalextension.Split(',');
            for (int i = 0; i < excExt.Length; i++)
            {
                if (excExt[i].ToLower() == _fileExt.ToLower())
                {
                    return false;
                }
            }
            //检查合法文件
            string[] allowExt = (entity.imgextension + "," + entity.videoextension + "," + entity.fileextension).Split(',');
            for (int i = 0; i < allowExt.Length; i++)
            {
                if (allowExt[i].ToLower() == _fileExt.ToLower())
                {
                    return true;
                }
            }
            return false;
        }
        /// <summary>
        /// 检查文件大小是否合法
        /// </summary>
        /// <param name="_fileExt">文件扩展名，不含“.”</param>
        /// <param name="_fileSize">文件大小(B)</param>
        /// <param name="entity">系统上传配置信息</param>
        /// <returns></returns>
        private bool CheckFileSize(string _fileExt, long _fileSize, FileUploadConfigEntity entity)
        {
            //将视频扩展名转换成ArrayList
            ArrayList lsVideoExt = new ArrayList(entity.videoextension.ToLower().Split(','));
            //判断是否为图片文件
            if (IsImage(_fileExt, entity))
            {
                if (entity.imgsize > 0 && _fileSize > entity.imgsize * 1024)
                {
                    return false;
                }
            }
            else if (lsVideoExt.Contains(_fileExt.ToLower()))
            {
                if (entity.videosize > 0 && _fileSize > entity.videosize * 1024)
                {
                    return false;
                }
            }
            else
            {
                if (entity.filesize > 0 && _fileSize > entity.filesize * 1024)
                {
                    return false;
                }
            }
            return true;
        }
        /// <summary>
        /// 是否为图片文件
        /// </summary>
        /// <param name="_fileExt">文件扩展名，不含“.”</param>
        /// <param name="entity">系统上传配置信息</param>
        /// <returns></returns>
        private bool IsImage(string _fileExt, FileUploadConfigEntity entity)
        {
            ArrayList lsImgExt = new ArrayList(entity.imgextension.Split(','));
            if (lsImgExt.Contains(_fileExt.ToLower()))
            {
                return true;
            }
            return false;
        }
        /// <summary>
        /// 获取文件的Content type
        /// </summary>
        /// <param name="fileName">文件全名称</param>
        /// <returns></returns>
        private string GetContentType(string fileName)
        {
            var ext = Path.GetExtension(fileName).ToLower();
            //获取文件的ContentType
            var provider = new FileExtensionContentTypeProvider();
            var contentType = provider.Mappings[ext];
            if (string.IsNullOrEmpty(contentType))
            {
                contentType = "application/octet-stream";
            }
            return contentType;
        }
        #endregion
    }
}
